from ckeditor_uploader.fields import RichTextUploadingField
from django.contrib.auth.models import AbstractUser
from django.db.models import Model, CharField, DateTimeField, ForeignKey, IntegerField, CASCADE, FileField, \
    TextField, BooleanField, PROTECT, EmailField, URLField, GenericIPAddressField
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from mptt.models import MPTTModel

from chatbot.const import const


class Tenant(Model):
    name = CharField(_('Customer Name'), max_length=100, unique=True)
    email = EmailField(_('Email'))
    logo = FileField(_('Logo'), upload_to='upload/%Y/%m/%d/', max_length=200)
    url = URLField(_('Web Site'))

    created_at = DateTimeField(default=timezone.now, verbose_name=_('created_at'))
    creator = IntegerField(null=True, blank=False, verbose_name=_('creator'))
    modified_at = DateTimeField(default=timezone.now, verbose_name='modified_at')
    modifier = IntegerField(null=True, blank=False, verbose_name=_('modifier'))

    class Meta:
        ordering = ['-modified_at', 'name']
        verbose_name = _('Company Profile')
        verbose_name_plural = _('Company Profile')

    def __str__(self):
        return self.name


class User(AbstractUser):
    avatar = FileField(_('Avatar'), upload_to='upload/%Y/%m/%d/', max_length=200)
    alias = CharField(_('Nickname'), null=True, blank=True, max_length=200)
    gender = CharField(_('Gender'), null=True, blank=True, max_length=200)
    nationality = CharField(_('Nationality'), null=True, blank=True, max_length=200)
    city = CharField(_('City'), null=True, blank=True, max_length=200)
    qq = CharField('QQ', null=True, blank=True, max_length=200)
    phone = CharField(_('Phone'), null=True, blank=True, max_length=200)
    wechat = CharField(_('Wechat'), null=True, blank=True, max_length=200)
    weibo = CharField(_('Weibo'), null=True, blank=True, max_length=200)
    department = CharField(_('Department'), null=True, blank=True, max_length=200)
    title = CharField(_('Title'), null=True, blank=True, max_length=200)
    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='chatbot_user_tenant')

    creator = ForeignKey('self', verbose_name=_('creator'), on_delete=CASCADE, related_name='chatbot_user_creator',
                         null=True)
    modifier = ForeignKey('self', on_delete=CASCADE, related_name='chatbot_user_modifier', null=True,
                          verbose_name=_('modifier'))
    modified_at = DateTimeField(default=timezone.now, verbose_name=_('modified_at'))

    class Meta:
        verbose_name = _('User')
        verbose_name_plural = _('Users')
        db_table = 'chatbot_user'


class Classification(MPTTModel):
    name = CharField(_('Name'), max_length=100, unique=True)

    parent = ForeignKey('self', verbose_name=_('Parent'), related_name='parent_classification', on_delete=CASCADE,
                        null=True,
                        blank=True)

    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='classification_tenant', )

    created_at = DateTimeField(default=timezone.now, verbose_name=_('created_at'))
    creator = ForeignKey(User, on_delete=CASCADE, related_name='classification_creator', null=True,
                         blank=False, verbose_name=_('creator'))
    modified_at = DateTimeField(default=timezone.now, verbose_name=_('modified_at'))
    modifier = ForeignKey(User, on_delete=CASCADE, related_name='classification_modifier', null=True,
                          verbose_name=_('modifier'))

    class Meta:
        verbose_name = _('Classification')
        verbose_name_plural = _('Classifications')

    def __str__(self):
        return self.name


class BasicConfiguration(Model):
    conf_type = IntegerField(_('Configuration Type'), choices=const.CONFIGURATION_TYPE,
                             default=const.CONFIGURATION_TYPE_FUNCTION_SWITCH)
    code = CharField(_('Code'), max_length=100, unique=True)
    name = CharField(_('Name'), max_length=200, unique=True)
    value = CharField(_('Value'), max_length=4000)
    desc = TextField(_('Description'), max_length=4000)
    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='basic_configuration_tenant', )
    creator = ForeignKey(User, verbose_name=_('creator'), on_delete=CASCADE, related_name='basic_configuration_creator',
                         null=True)
    created_at = DateTimeField(default=timezone.now, verbose_name=_('created_at'))
    modifier = ForeignKey(User, on_delete=CASCADE, related_name='basic_configuration_modifier', null=True,
                          verbose_name=_('modifier'))
    modified_at = DateTimeField(default=timezone.now, verbose_name=_('modified_at'))

    class Meta:
        ordering = ['name', 'code']
        verbose_name = _('Basic Configuration')
        verbose_name_plural = _('Basic Configurations')
        db_table = 'chatbot_basic_configuration'

    def __str__(self):
        return self.name


class SkillConfiguration(Model):
    skill_type = IntegerField(_('Skill Type'), choices=const.SKILL_TYPE, default=const.SKILL_TYPE_LOGIC_ADAPTER)
    name = CharField(_('Name'), max_length=200, unique=True)
    code = CharField(_('Code'), max_length=100, unique=True)
    path = CharField(_('Import Path'), max_length=1000)
    state = BooleanField(_('State'))
    desc = TextField(_('Description'), max_length=4000)
    seq = IntegerField(_('Sequence'), default=0)

    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='skill_configuration_tenant', )
    creator = ForeignKey(User, verbose_name=_('creator'), on_delete=CASCADE, related_name='skill_configuration_creator',
                         null=True)
    created_at = DateTimeField(default=timezone.now, verbose_name=_('created_at'))
    modifier = ForeignKey(User, on_delete=CASCADE, related_name='skill_configuration_modifier', null=True,
                          verbose_name=_('modifier'))
    modified_at = DateTimeField(default=timezone.now, verbose_name=_('modified_at'))

    class Meta:
        ordering = ['skill_type', 'seq']
        verbose_name = _('Skill Configuration')
        verbose_name_plural = _('Skill Configurations')
        db_table = 'chatbot_skill_configuration'

    def __str__(self):
        return self.name


class ScriptConfiguration(Model):
    name = CharField(_('Name'), max_length=200, unique=True)
    code = CharField(_('Code'), max_length=100, unique=True)
    desc = TextField(_('Description'), max_length=4000)

    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='script_configuration_tenant', )
    creator = ForeignKey(User, verbose_name=_('creator'), on_delete=CASCADE,
                         related_name='script_configuration_creator', null=True)
    created_at = DateTimeField(default=timezone.now, verbose_name=_('created_at'))
    modifier = ForeignKey(User, on_delete=CASCADE, related_name='script_configuration_modifier', null=True,
                          verbose_name=_('modifier'))
    modified_at = DateTimeField(default=timezone.now, verbose_name=_('modified_at'))

    class Meta:
        ordering = ['name', 'code']
        verbose_name = _('Script Configuration')
        verbose_name_plural = _('Script Configurations')
        db_table = 'chatbot_script_configuration'

    def __str__(self):
        return self.name


class Script(Model):
    configuration = ForeignKey(ScriptConfiguration, verbose_name=_('Configuration Belong to'), on_delete=CASCADE,
                               null=True, blank=True, related_name='script_configuration')
    script = TextField(_('Script'), max_length=20000, null=False, blank=False)
    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='script_tenant')

    creator = ForeignKey(User, verbose_name=_('creator'), on_delete=CASCADE, related_name='script_creator', null=True)
    created_at = DateTimeField(default=timezone.now, verbose_name=_('created_at'))
    modifier = ForeignKey(User, on_delete=CASCADE, related_name='script_modifier', null=True,
                          verbose_name=_('modifier'))
    modified_at = DateTimeField(default=timezone.now, verbose_name=_('modified_at'))

    class Meta:
        verbose_name = _('Script')
        verbose_name_plural = _('Scripts')
        db_table = 'chatbot_script'

    def __str__(self):
        return self.script


class Vocabulary(Model):
    word = CharField(_('Word'), max_length=1000)
    word_type = IntegerField(_('Type'), choices=const.WORD_TYPE, default=const.WORD_TYPE_NORMAL_WORD, )
    word_from = IntegerField(_('Source'), choices=const.WORD_SOURCE, default=const.WORD_SOURCE_CUSTOMER_ADD)
    weight = IntegerField(_('Weight'), default=70)
    parent = ForeignKey('self', verbose_name=_('Parent'), related_name='parent_word', on_delete=CASCADE, null=True,
                        blank=True)
    relation_type = IntegerField(_('Relation Type'), choices=const.WORD_RELATION_TYPE,
                                 default=const.WORD_RELATION_TYPE_SIMILARITY,
                                 null=True, blank=True)
    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='vocabulary_tenant', )
    creator = ForeignKey(User, verbose_name=_('creator'), on_delete=CASCADE, related_name='word_creator', null=True)
    created_at = DateTimeField(default=timezone.now, verbose_name=_('created_at'))
    modifier = ForeignKey(User, on_delete=CASCADE, related_name='word_modifier', null=True, verbose_name=_('modifier'))
    modified_at = DateTimeField(default=timezone.now, verbose_name=_('modified_at'))

    def column_word_save(self, word, user):
        self.word_from = const.PROPERTY
        self._set_default_value(word, user)
        self._is_exists_in_db(word)

    def entity_word_save(self, word, user):
        self.word_from = const.ENTITY
        self._set_default_value(word, user)
        self._is_exists_in_db(word)

    def value_word_save(self, word, user, parent):

        self.word_from = const.VALUE
        self.word_type = const.HYPONYM
        self.parent = parent
        self._set_default_value(word, user)
        self._is_exists_in_db(word)

    def _is_exists_in_db(self, word):
        db_words = Vocabulary.objects.filter(word=word)
        if db_words:
            self.id = db_words[0].id
        else:
            super().save()

    def _set_default_value(self, word, user):
        self.word = word
        self.creator = user
        self.modifier = user

    class Meta:
        unique_together = [['word', 'word_type']]
        ordering = ['word_type', 'word', ]
        verbose_name = _('Vocabulary')
        verbose_name_plural = _('Vocabulary')
        db_table = 'chatbot_vocabulary'

    def __str__(self):
        return self.word


class Knowledge(Model):
    title = CharField(_('Standard Question'), max_length=200, unique=True)

    classification = ForeignKey(Classification, on_delete=CASCADE, related_name='knowledge_classification', null=True,
                                blank=False, verbose_name=_('Classification Belonged To'))

    state = IntegerField(_('State'), choices=const.STATE, default=const.STATE_TO_BE_EFFECTIVE)

    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='knowledge_tenant', )

    created_at = DateTimeField(default=timezone.now, verbose_name=_('created_at'))
    creator = ForeignKey(User, verbose_name=_('creator'), on_delete=CASCADE, related_name='knowledge_creator',
                         null=True,
                         blank=False)
    modified_at = DateTimeField(default=timezone.now, verbose_name=_('modified_at'))
    modifier = ForeignKey(User, on_delete=CASCADE, related_name='knowledge_modifier', null=True,
                          verbose_name=_('modifier'))

    def similar_question_count(self):
        self.similarquestion_set.filter(knowledge=self.id)
        return self.similarquestion_set.count()

    similar_question_count.short_description = _('Similar Question Count')

    @property
    def answer(self):
        answers = self.questionanswer_set.all()
        if answers:
            return answers[0].answer
        return ""

    class Meta:
        ordering = ['-modified_at', 'title']
        db_table = 'chatbot_knowledge'
        verbose_name = 'FAQ'
        verbose_name_plural = 'FAQ'

    def __str__(self):
        return self.title


class SimilarQuestion(Model):
    question = CharField('Q', max_length=200, unique=True)
    knowledge = ForeignKey(Knowledge, on_delete=CASCADE)

    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='similar_question_tenant', )

    created_at = DateTimeField(default=timezone.now, verbose_name=_('created_at'))
    creator = ForeignKey(User, verbose_name=_('creator'), on_delete=CASCADE, related_name='similar_question_creator',
                         null=True)
    modified_at = DateTimeField(default=timezone.now, verbose_name=_('modified_at'))
    modifier = ForeignKey(User, on_delete=CASCADE, related_name='similar_question_modifier',
                          null=True, verbose_name=_('modifier'))

    class Meta:
        ordering = ['-modified_at', 'question']
        db_table = 'chatbot_similar_question'
        verbose_name = _('Similar Question')
        verbose_name_plural = _('Similar Questions')

    def __str__(self):
        return self.question


class QuestionAnswer(Model):
    answer = RichTextUploadingField('A', max_length=20000)

    knowledge = ForeignKey(Knowledge, on_delete=CASCADE)

    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='question_answer_tenant', )

    creator = ForeignKey(User, verbose_name=_('creator'), on_delete=CASCADE, related_name='question_answer_creator',
                         null=True)
    created_at = DateTimeField(default=timezone.now, verbose_name=_('created_at'))
    modifier = ForeignKey(User, on_delete=CASCADE, related_name='question_answer_modifier', null=True,
                          verbose_name=_('modifier'))
    modified_at = DateTimeField(default=timezone.now, verbose_name=_('modified_at'))

    class Meta:
        ordering = ['-modified_at']
        db_table = 'chatbot_question_answer'
        verbose_name = _('Answer')
        verbose_name_plural = _('Answers')


class Table(Model):
    name = CharField(_('Name'), max_length=200, unique=True)
    file = FileField(_('File'), upload_to='upload/%Y/%m/%d/', max_length=200, null=False, blank=False)
    state = IntegerField(_('State'), choices=const.STATE, default=const.STATE_ACTIVE)

    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='table_tenant', )

    creator = ForeignKey(User, verbose_name=_('creator'), on_delete=CASCADE, related_name='table_creator', null=True)
    created_at = DateTimeField(default=timezone.now, verbose_name=_('created_at'))
    modifier = ForeignKey(User, on_delete=CASCADE, related_name='table_modifier', null=True, verbose_name=_('modifier'))
    modified_at = DateTimeField(default=timezone.now, verbose_name=_('modified_at'))

    class Meta:
        ordering = ['-modified_at', 'name']
        verbose_name = _('Table')
        verbose_name_plural = _('Tables')
        db_table = 'chatbot_kg_table'

    def __str__(self):
        return self.name


class TableColumn(Model):
    name = CharField(_('Name'), max_length=400)
    table = ForeignKey(Table, on_delete=CASCADE, related_name='table_of_column', verbose_name=_('Table'))
    seq = IntegerField(_('Sequence'))
    col_type = IntegerField(_('Type'), choices=const.SLOT_TYPE, default=const.SLOT_TYPE_TEXT)
    searchable = BooleanField(_('Searchable'), default=False)
    clarify_words = CharField(_('Clarify Words'), max_length=200)
    is_primary_key = BooleanField(_('Is Primary Key'), default=False)
    word = ForeignKey(Vocabulary, related_name='column_vocabulary', on_delete=PROTECT, null=True)
    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='table_column_tenant', )

    class Meta:
        unique_together = [['table', 'name']]
        ordering = ['seq']
        verbose_name = _('Table Property')
        verbose_name_plural = _('Table Properties')
        db_table = 'chatbot_kg_table_column'

    def __str__(self):
        return self.name

    def save_from_header(self, header, col_type, user, index, table):
        self.name = header
        self.col_type = col_type
        self.clarify_words = _('Please Supply: ') + header
        self.seq = index
        if index == 1:
            self.is_primary_key = True
            self.searchable = True
        if self.col_type in (
                const.DATE, const.ENUM, const.PERSON, const.INTEGER, const.PRICE, const.ORG, const.CITY,
                const.BRAND
        ):
            self.searchable = True
        self.table = table
        word = Vocabulary()
        word.column_word_save(header, user)
        self.word = word
        super().save()


class TableColumnValue(Model):
    value = TextField(_('Value'), max_length=20000, null=True, blank=True)
    table = ForeignKey(Table, on_delete=CASCADE, related_name='value_of_table', verbose_name=_('Table Name'))
    col = ForeignKey(TableColumn, on_delete=CASCADE, related_name='value_of_column', verbose_name='Column Name')
    row_id = IntegerField('Row ID')
    word = ForeignKey(Vocabulary, related_name='value_vocabulary', on_delete=PROTECT, null=True, blank=True)
    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='table_column_value_tenant', )

    class Meta:
        unique_together = [['col', 'row_id']]
        ordering = ['table', 'row_id', 'col', 'value']
        verbose_name = _('Table Property Value')
        verbose_name_plural = _('Table Property Values')
        db_table = 'chatbot_kg_table_column_value'

    def __str__(self):
        if self.value:
            return self.value
        else:
            return str(self.id)

    def save_from_value(self, value, user, row_num, table, column):
        self.value = value
        self.table = table
        self.col = column
        self.row_id = row_num
        if value:
            word = Vocabulary()
            word.column_word_save(value, user)
            self.word = word
        super().save()


class ChatVisitor(Model):
    username = CharField(_('Username'), null=True, blank=True, max_length=200)
    avatar = FileField(_('Avatar'), upload_to='upload/%Y/%m/%d/', max_length=200)
    account = CharField(_('Account'), null=True, blank=True, max_length=200)
    alias = CharField(_('Nickname'), null=True, blank=True, max_length=200)
    gender = CharField(_('Gender'), null=True, blank=True, max_length=200)
    nationality = CharField(_('Nationality'), null=True, blank=True, max_length=200)
    city = CharField(_('City'), null=True, blank=True, max_length=200)
    qq = CharField('QQ', null=True, blank=True, max_length=200)
    email = EmailField(_('Email'), null=True, blank=True, max_length=200)
    phone = CharField(_('Phone'), null=True, blank=True, max_length=200)
    wechat = CharField(_('Wechat'), null=True, blank=True, max_length=200)
    weibo = CharField(_('Weibo'), null=True, blank=True, max_length=200)
    department = CharField(_('Department'), null=True, blank=True, max_length=200)
    title = CharField(_('Title'), null=True, blank=True, max_length=200)
    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='chat_visitor_tenant')

    created_at = DateTimeField(default=timezone.now, verbose_name=_('created_at'))
    modified_at = DateTimeField(default=timezone.now, verbose_name=_('modified_at'))

    class Meta:
        verbose_name = _('Visitor')
        verbose_name_plural = _('Visitors')
        db_table = 'chatbot_visitor'

    def __str__(self):
        return self.username


class Channel(Model):
    name = CharField(_('Name'), null=True, blank=True, max_length=200)
    cls = IntegerField(_('Type'), null=True, blank=True, default=const.CHANNEL_TYPE_WEB,
                       choices=const.CHANNEL_TYPE)
    bot_name = CharField(_('Bot Name'), null=False, blank=False, max_length=50, default=_('Little J'))
    bot_avatar = FileField(_('Bot Avatar'), upload_to='upload/%Y/%m/%d/', max_length=200, blank=True, null=True)

    state = IntegerField(_('State'), choices=const.STATE, default=const.STATE_ACTIVE)
    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='channel_tenant')

    creator = ForeignKey(User, verbose_name=_('creator'), on_delete=CASCADE, related_name='channel_creator', null=True)
    created_at = DateTimeField(default=timezone.now, verbose_name=_('created_at'))
    modifier = ForeignKey(User, on_delete=CASCADE, related_name='channel_modifier', null=True,
                          verbose_name=_('modifier'))
    modified_at = DateTimeField(default=timezone.now, verbose_name=_('modified_at'))

    class Meta:
        verbose_name = _('Channel')
        verbose_name_plural = _('Channels')
        db_table = 'chatbot_channel'

    def __str__(self):
        return self.name


class ChatSession(Model):
    start_time = DateTimeField(verbose_name='Started At', default=timezone.now)
    visitor = ForeignKey(ChatVisitor, on_delete=CASCADE, related_name='chat_session_visitor', null=True,
                         blank=False, verbose_name=_('Visitor'))
    channel = ForeignKey(Channel, on_delete=CASCADE, related_name='chat_session_channel', null=True,
                         blank=False, verbose_name=_('Channel'))
    end_time = DateTimeField(default=timezone.now, verbose_name=_('Ended At'))
    dialog_turns = IntegerField(_('Dialog Turns'), default=0)
    fingerprint = CharField(_('Fingerprint'), max_length=1000, blank=True, null=True)
    ip = GenericIPAddressField(_('IP'), null=True)
    satisfaction = IntegerField(_('Satisfaction'), blank=True, null=True, default=const.SATISFACTION_NOT_EVALUATED,
                                choices=const.SATISFACTION)
    dissatisfaction_reason = CharField(_('Dissatisfaction Reason'), max_length=50, blank=True, null=True)
    additional_reason = TextField(_('Additional'), max_length=2000, blank=True, null=True)
    agent_type = IntegerField(_('Agent Type'), null=False, default=const.AGENT_TYPE_ROBOT, choices=const.AGENT_TYPE)
    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='chat_session_tenant')

    class Meta:
        verbose_name = _('Session')
        verbose_name_plural = _('Sessions')
        db_table = 'chatbot_chat_session'

    def __str__(self):
        return self.start_time


class ChatLog(Model):
    chat_session = ForeignKey(ChatSession, on_delete=CASCADE, related_name='chat_log_session', null=True,
                              blank=False, verbose_name=_('Session'))
    question = CharField(_('Visitor Question'), max_length=1000, null=False)
    question_type = IntegerField(_('Question Type'), null=False, default=const.QUESTION_TYPE_TEXT,
                                 choices=const.QUESTION_TYPE)
    answer = TextField(_('Reply'), max_length=20000)
    answer_source = IntegerField(_('Type Of Reply Based On'), null=False, default=const.ANSWER_SOURCE_FIXED_SCRIPT,
                                 choices=const.ANSWER_SOURCE)
    answer_source_id = CharField(_('Knowledge Based On'), max_length=1000, null=False)
    match_type = IntegerField(_('Match Type'), null=False, default=const.MATCH_TYPE_DIRECTLY_REPLY,
                              choices=const.MATCH_TYPE)
    match_source_type = IntegerField(_('Knowledge Type Matched'), null=False,
                                     default=const.MATCH_SOURCE_TYPE_FIXED_SCENE,
                                     choices=const.MATCH_SOURCE_TYPE)
    ask_time = DateTimeField(default=timezone.now, verbose_name=_('Asked At'))
    answer_time = DateTimeField(default=timezone.now, verbose_name=_('Replied At'))

    tenant = ForeignKey(Tenant, verbose_name=_('Company Belonged To'), on_delete=CASCADE, null=True, blank=True,
                        related_name='chat_log_tenant')

    class Meta:
        verbose_name = _('ChatLog')
        verbose_name_plural = _('ChatLogs')
        db_table = 'chatbot_chat_log'

    def __str__(self):
        return self.question
